#include "http_conn.hpp"

//状态信息
const char* ok_200_title = "OK";
const char* error_400_title = "Bad Request";
const char* error_400_form = "Your request has bad syntax or is inherently impossible to satisfy.\n";
const char* error_403_title = "Forbiddent";
const char* error_403_form = "You do not have permission to get file from this server.\n";
const char* error_404_title = "Not Found";
const char* error_404_form = "The requested file was not found on this server.\n";
const char* error_500_title = "Internal Error";
const char* error_500_form = "There was an unusual problem serving the requested file.\n";

//网站的根目录
const char* doc_root = "./wwwroot/";

//设置非阻塞
int setNonBlock(int fd)
{
    int old_op = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, old_op | O_NONBLOCK);
    return old_op;
}

//添加事件
void addEvent(int epfd, int sock, bool epolloneshot)
{
    struct epoll_event event;
    event.data.fd = sock;
    event.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
    if (epolloneshot)
        event.events |= EPOLLONESHOT;
    assert(epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &event) != -1);
    setNonBlock(sock);
}

//修改事件
void modEvent(int epfd, int sock, int ev)
{
    struct epoll_event event;
    event.data.fd = sock;
    event.events = EPOLLET | EPOLLRDHUP | EPOLLONESHOT | ev;
    assert(epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &event) != -1);
}

//删除事件
void delEvent(int epfd, int sock)
{
    assert(epoll_ctl(epfd, EPOLL_CTL_DEL, sock, NULL) != -1);
    close(sock);
}

void http_conn::close_conn(bool real_close)
{
    if (real_close && m_confd != -1)
    {
        delEvent(m_epfd, m_confd);
        m_confd = -1;
        m_user_cnt--;
    }
}

void http_conn::init(struct sockaddr_in& address, int sockfd)
{
    m_client = address;
    m_confd = sockfd;
    //用于调试时使用
    // int reuse = 1;
    // setsockopt(m_confd, SOL_SOCKET, SO_REUSEPORT, &reuse, sizeof(reuse));
    addEvent(m_epfd, m_confd, true);
    m_user_cnt++;
    init();
}

void http_conn::init()
{
    memset(m_rd_buffer, '\0', RD_BUFFER_SIZE);
    memset(m_wr_buffer, '\0', WR_BUFFER_SIZE);
    memset(m_file_path, '\0', FILE_NAME_SIZE);
    memset(m_iov, 0, sizeof(m_iov));
    m_read_x = 0;
    m_check_x = 0;
    m_start_line = 0;
    m_write_x = 0;
    m_check_state = REQUEST_LINE;
    m_method = GET;
    m_url = NULL;
    m_version = NULL;
    m_host = NULL;
    m_content_length = 0;
    m_keep_live = false;
    m_file_address = NULL;
    m_iov_cnt = 0;
}

//读数据
bool http_conn::read_()
{
    if (m_read_x >= RD_BUFFER_SIZE)
    {
        return false; 
    }
    int ret = 0;
    while (true)
    {
        ret = recv(m_confd, m_rd_buffer + m_read_x, RD_BUFFER_SIZE - m_read_x, 0);
        printf("ret = %d\n", ret);
        if (ret == 0)
        {
            printf("对端已经关闭连接\n");
            return false;
        } 
        else if (ret < 0)
        {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                break;
            printf("数据读取失败\n");
            return false;
        }
        m_read_x += ret;
        printf("正在读取数据 %d\n", m_read_x);
    }
    printf("数据读取完成\n");
    return true;
}

//处理成完整一行
LINE_STATE http_conn::parse_line()
{
    for (;m_check_x < m_read_x;m_check_x++)
    {
        //遇到\r
        if (m_rd_buffer[m_check_x] == '\r')
        {
            //数据没有读完，还需要继续读数据
            if (m_check_x + 1 == m_read_x)
            {
                return LINE_OPEN;
            }
            if (m_rd_buffer[m_check_x + 1] == '\n')
            {
                m_rd_buffer[m_check_x++] = '\0';
                m_rd_buffer[m_check_x++] = '\0';
                return LINE_OK;
            }
            else
                return LINE_BAD;
        }
        //遇到\n
        if (m_rd_buffer[m_check_x] == '\n')
        {
            if (m_check_x >= 1 && m_rd_buffer[m_check_x - 1] == '\r')
            {
                m_rd_buffer[m_check_x - 1] = '\0';
                m_rd_buffer[m_check_x++] = '\0';
                return LINE_OK;
            }
            else
                return LINE_BAD;
        }
    }
    return LINE_OPEN;
}

//解析请求行
HTTP_CODE http_conn::parse_request_line(char* text)
{
    assert(text);
    char* tmp = text;

    //解析出请求方法
    if (strstr(tmp, "GET") != NULL)
    {
        m_method = GET;
        tmp += strlen("GET");
        *tmp++ = '\0';
    }
    else if (strstr(tmp, "POST") != NULL)
    {
        m_method = POST;
        tmp += strlen("POST");
        *tmp++ = '\0';
    }
    else
        return BAD_REQUEST;
    
    printf("method: %d\n", m_method);

    m_url = tmp;
    //解析http协议版本
    if ((tmp = strstr(tmp, "HTTP/1.1")) != NULL)
    {
        m_version = tmp;
    }
    else
        BAD_REQUEST;
    
    printf("version: %s\n", m_version);

    //解析出url
    char* m = NULL;
    if ((m = strchr(m_url, ' ')) != NULL)
    {
        *m = '\0';
        if (strchr(m_url, '/') != NULL)
        {
            m_url += 1;
        }
        else
            return BAD_REQUEST;
    }
    else
        return BAD_REQUEST;

    printf("url: %s\n", m_url);
    //转移状态
    m_check_state = REQUEST_HEADER;
    return NO_REQUEST;
}

//解析请求属性
HTTP_CODE http_conn::parse_request_header(char* text)
{
    assert(text);
    char* tmp = text;

    //遇到空行了
    if (text[0] == '\0')
    {
        //没有正文，则解析完成
        if (m_content_length == 0)
            return GET_REQUEST;
        else
        {
            m_check_state = REQUEST_CONTENT;
            return NO_REQUEST;
        }
    }
    //解析主机名
    else if (strstr(tmp, "Host: ") != NULL)
    {
        tmp += strlen("Host: ");
        m_host = tmp;

        printf("host: %s\n", m_host);
    }
    //解析长连接
    else if (strstr(tmp, "Connection: ") != NULL)
    {
        printf("解析长连接\n");
        tmp += strlen("Connection: ");
        if (strstr(tmp, "keep-alive") != NULL)
            m_keep_live = true;

        printf("keep-live: %d\n", m_keep_live);
    }
    //解析长度
    else if (strstr(tmp, "Content-Length: ") != NULL)
    {
        tmp += strlen("Content-Length: ");
        m_content_length = atoi(tmp);

        printf("content-length: %d\n", m_content_length);
    }
    else
    {}
        //printf("unknow header %s\n", text);

    return NO_REQUEST;
}

//解析请求内容
HTTP_CODE http_conn::parse_content(char* text)
{
    assert(text);
    //数据读完了
    if (m_read_x >= m_content_length + m_check_x)
    {
        text[m_content_length] = '\0';
        return GET_REQUEST;
    }
    return NO_REQUEST;
}

//处理整个http报文
HTTP_CODE http_conn::process_read()
{
    printf("处理http报文\n");
    LINE_STATE line_state = LINE_OK;
    HTTP_CODE ret = NO_REQUEST;
    char* text;
    while ((line_state = parse_line()) == LINE_OK || (line_state == LINE_OK && m_check_state == REQUEST_CONTENT))
    {
        text = get_line();
        m_start_line = m_check_x;
        HTTP_CODE ret;
        switch (m_check_state)
        {
        case REQUEST_LINE:
            printf("处理请求行\n");
            printf("%s\n", text);
            ret = parse_request_line(text);
            if (ret == BAD_REQUEST)
                return BAD_REQUEST;
            break;
        case REQUEST_HEADER:
            printf("处理请求属性\n");
            ret = parse_request_header(text);
            if (ret == BAD_REQUEST)
                return BAD_REQUEST;
            else if (ret == GET_REQUEST)
                return do_request();
            else
                break;
        case REQUEST_CONTENT:
            printf("处理请求内容\n");
            ret = parse_content(get_line());
            if (ret == GET_REQUEST)
                return do_request();
            line_state = LINE_OPEN;
            break;
        default:
            return INTERNAL_ERROR;
        }
    }
    return NO_REQUEST;
}

HTTP_CODE http_conn::do_request()
{
    strcpy(m_file_path, doc_root);
    size_t len = strlen(m_file_path);
    strcpy(m_file_path + len, m_url);

    //不存在该文件
    if (stat(m_file_path, &m_file_state) < 0)
    {
        return NO_RESOURCE;
    }
    //其他用户是否具有可读权限
    if (!(m_file_state.st_mode & S_IROTH))
    {
        return FORBIDDEN_REQUEST;
    }
    //是否是目录文件
    if (S_ISDIR(m_file_state.st_mode))
    {
        return BAD_REQUEST;
    }
    int fd = open(m_file_path, O_RDONLY);
    m_file_address = (char*)mmap(NULL, m_file_state.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);
    return FILE_REQUEST;
}

bool http_conn::add_response(const char* format, ...)
{
    if (m_write_x >= WR_BUFFER_SIZE)
    {
        return false;
    }
    va_list arg_list;
    va_start(arg_list, format);
    int len = vsnprintf(m_wr_buffer + m_write_x, WR_BUFFER_SIZE - m_write_x - 1, format, arg_list);
    if (len >= WR_BUFFER_SIZE - m_write_x - 1)
    {
        return false;
    }
    m_write_x += len;
    va_end(arg_list);
    return true;
}

bool http_conn::add_response_status(int status_code, const char* title)
{
    return add_response("%s %d %s\r\n", "HTTP/1.1", status_code, title);
}

bool http_conn::add_response_header(int content_len)
{
    add_content_length(content_len);
    add_linger();
    add_response_blank();
    return true;
}

bool http_conn::add_response_blank()
{
    return add_response("%s", "\r\n");
}

bool http_conn::add_content_length(int content_len)
{
    return add_response("Content-Length: %d\r\n", content_len);
}

bool http_conn::add_linger()
{
    return add_response("Connection: %s\r\n", m_keep_live ? "keep-alive" : "close");
}

bool http_conn::add_response_content(const char* content)
{
    return add_response("%s", content);
}

bool http_conn::process_write(HTTP_CODE ret)
{
    switch (ret)
    {
    case INTERNAL_ERROR:
        add_response_status(500, error_500_title);
        add_response_header(strlen(error_500_form));
        if (!add_response_content(error_500_form))
            return false;
        break;
    case FORBIDDEN_REQUEST:
        add_response_status(403, error_403_title);
        add_response_header(strlen(error_403_form));
        if (!add_response_content(error_403_form))
            return false;
        break;
    case BAD_REQUEST:
        add_response_status(400, error_400_title);
        add_response_header(strlen(error_400_form));
        if (!add_response_content(error_400_form))
            return false;
        break;
    case NO_RESOURCE:
        add_response_status(404, error_404_title);
        add_response_header(strlen(error_404_form));
        if (!add_response_content(error_404_form))
            return false;
        break;
    case FILE_REQUEST:
        add_response_status(200, ok_200_title);
        if (m_file_state.st_size != 0)
        {
            add_response_header(m_file_state.st_size);
            m_iov[0].iov_base = m_wr_buffer;
            m_iov[0].iov_len = m_write_x;
            m_iov[1].iov_base = m_file_address;
            m_iov[1].iov_len = m_file_state.st_size;
            m_iov_cnt = 2;
            return true;
        }
        else
        {
            const char* content = "<html><body></body></html>";
            size_t len = strlen(content);
            add_response_header(len);
            if (!add_response_content(content))
            {
                return false;
            }
        }
    default:
        return false;
    }
    m_iov[0].iov_base = m_wr_buffer;
    m_iov[0].iov_len = m_write_x;
    m_iov_cnt = 1;
    return true;
}

void http_conn::process()
{
    HTTP_CODE read_ret = process_read();
    if (read_ret == NO_REQUEST)
    {
        modEvent(m_epfd, m_confd, EPOLLIN);
        return;
    }
    bool write_ret = process_write(read_ret);
    if (!write_ret)
    {
        close_conn();
        return;
    }
    modEvent(m_epfd, m_confd, EPOLLOUT);
}

bool http_conn::write_()
{
    int send_to_bytes = m_write_x;
    int have_to_send = 0;
    int temp = 0;
    //没有数据可写
    if (send_to_bytes == 0)
    {
        modEvent(m_epfd, m_confd, EPOLLIN);
        init();
        return true;
    }

    while (true)
    {
        temp = writev(m_confd, m_iov, m_iov_cnt);
        if (temp < 0)
        {
            if (errno == EAGAIN)
            {
                modEvent(m_epfd, m_confd, EPOLLIN);
                return true;
            }
            unmap();
            return false;
        }
        send_to_bytes -= temp;
        have_to_send += temp;
        if (send_to_bytes <= have_to_send)
        {
            if (m_keep_live)
            {
                init();
                modEvent(m_epfd, m_confd, EPOLLIN);
                return true;
            }
            else
            {
                modEvent(m_epfd, m_confd, EPOLLIN);
                return false;
            }
        }
    }
    return true;
}

void http_conn::unmap()
{
    if (m_file_address)
    {
        munmap(m_file_address, m_file_state.st_size);
        m_file_address = NULL;
    }
}
